package com.smart.manager.system.repository;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.smart.common.commder.SmartScanner;
import com.smart.common.entities.constant.ArcoTips;
import com.smart.common.entities.core.Page;
import com.smart.common.entities.option.SelectOption;
import com.smart.common.exception.SmartException;
import com.smart.manager.system.entities.body.RSystemApi;
import com.smart.manager.system.entities.models.SystemApi;
import com.smart.manager.system.entities.models.query.QSystemApi;
import io.ebean.DB;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Service
@Log4j2
@RequiredArgsConstructor
public class ApiRepository {

    private final SmartScanner smartScanner;

    public Page<SystemApi> pageList(RSystemApi.SearchBody search) {
        QSystemApi query = new QSystemApi();
        Opt.ofBlankAble(search.getModuleCode()).ifPresent(x -> query.module.code.eq(x));
        Opt.ofBlankAble(search.getUrl()).ifPresent(x -> query.url.contains(x));
        Opt.ofBlankAble(search.getTitle()).ifPresent(x -> query.title.contains(x));
        Opt.ofBlankAble(search.getExpr()).ifPresent(x -> query.expr.contains(x));
        Opt.ofNullable(search.getCustom()).ifPresent(x -> query.custom.eq(x));
        query.order().custom.desc().order().id.desc();
        query.setFirstRow(search.firstRow()).setMaxRows(search.maxRows());
        return new Page<SystemApi>().load(query.findPagedList());
    }

    public void add(RSystemApi.EditBody body) {

        SystemApi systemApi = BeanUtil.copyProperties(body, SystemApi.class);
        // 创建一个虚拟路径 用于计算MD
        String url = String.format("/virtual/%s/%s", systemApi.getModule().getCode(), RandomUtil.randomString(8));
        systemApi.setCustom(Boolean.TRUE).setRequestType("NONE").setUrl(url);
        String token = DigestUtil.md5Hex16(systemApi.getRequestType().concat(systemApi.getUrl()));
        SystemApi record = new QSystemApi().expr.eq(body.getExpr()).setMaxRows(1).findOne();
        Assert.isNull(record, "自定义权限创建失败,当前系统存在相同的权限表达式");
        systemApi.setToken(token).setUrl(url).save();
    }


    public void update(Long id, RSystemApi.EditBody body) {
        SystemApi record = new QSystemApi().id.eq(id).setMaxRows(1).findOne();
        Assert.notNull(record, ArcoTips.UPDATE_NOT_FOUND);
        Optional<SystemApi> oneOrEmpty = new QSystemApi().expr.eq(body.getExpr()).setMaxRows(1).findOneOrEmpty();
        oneOrEmpty.ifPresent(x -> {
            if (!x.getId().equals(id)) {
                String errMsg = String.format("修改失败： 权限表达式 %s 已存在", x.getExpr());
                throw new SmartException(errMsg);
            }
        });
        BeanUtil.copyProperties(body, record);
        Objects.requireNonNull(record).update();

    }

    public int delByIds(List<Long> ids) {

        if (ids.size() == 1) {
            return new QSystemApi().id.eq(ids.get(0)).delete();
        }
        List<SystemApi> list = new QSystemApi().id.in(ids).findList();
        Assert.notEmpty(list, ArcoTips.DELETE_NOT_FOUND);
        return DB.deleteAll(list);
    }


    /**
     * 对比更新功能权限
     *
     * @param newList 新列表
     */
    @Transactional(rollbackFor = Exception.class)
    public void contrastUpdate(List<SystemApi> newList) {
        List<SystemApi> oldList = new QSystemApi().custom.eq(false).findList();
        //  新数据 老数据都为空时 直接退出方法
        if (CollUtil.isEmpty(newList) && CollUtil.isEmpty(oldList)) {
            return;
        }
        // 没有最新数据 直接删除历史数据
        if (CollUtil.isEmpty(newList) && CollUtil.isNotEmpty(oldList)) {
            DB.deleteAll(oldList);
            return;
        }
        // 没有历史数据 只有新数据 直接全量新增新数据
        if (CollUtil.isEmpty(oldList) && CollUtil.isNotEmpty(newList)) {
            DB.insertAll(newList);
            return;
        }

        ArrayList<SystemApi> addList = new ArrayList<>();
        ArrayList<SystemApi> updateList = new ArrayList<>();
        ArrayList<SystemApi> delList = new ArrayList<>();
        List<String> newTokens = newList.stream().map(SystemApi::getToken).collect(Collectors.toList());
        List<String> oldTokens = oldList.stream().map(SystemApi::getToken).collect(Collectors.toList());

        newList.forEach(item -> {
            if (!oldTokens.contains(item.getToken())) {
                addList.add(item);
            } else {
                oldList.stream().filter(o -> StrUtil.equals(o.getToken(), item.getToken())
                                && !o.getTitle().equals(item.getTitle()))
                        .findFirst()
                        .ifPresent(x -> {
                            x.setTitle(item.getTitle());
                            updateList.add(x);

                        });

            }
        });

        oldList.forEach(item -> {
            if (!newTokens.contains(item.getToken())) {
                delList.add(item);
            }
        });

        if (addList.size() > 0) {
            DB.saveAll(addList);

        }

        if (updateList.size() > 0) {
            DB.updateAll(updateList);

        }
        if (delList.size() > 0) {
            DB.deleteAll(delList);

        }
    }


    /**
     * 获取功能列表字典
     *
     * @param showExpr 显示expr
     * @return {@code List<SelectOption>}
     */
    public List<SelectOption> getDictList(boolean showExpr) {
        final String tpl = "[%s]";
        QSystemApi query = new QSystemApi();
        return query.select(QSystemApi.Alias.title, QSystemApi.Alias.token, QSystemApi.Alias.expr)

                .order().sort.asc()
                .findStream()
                .map(item -> {
                    //存在权限表达式且需要显示表达式的时候加上表达式
                    boolean condition = StrUtil.isNotBlank(item.getExpr()) && showExpr;
                    String title = condition ? item.getTitle().concat(String.format(tpl, item.getExpr())) : item.getTitle();
                    return new SelectOption().setValue(item.getToken()).setLabel(title);
                })
                .collect(Collectors.toList());

    }


    /**
     * 扫描功能权限
     */
    @Transactional(rollbackFor = Exception.class)
    public void scanApi() {
        List<SystemApi> allApi = smartScanner.scanAllApi();
        this.contrastUpdate(allApi);
    }


    public String download() {
        List<SystemApi> list = new QSystemApi().order().expr.asc().findList();
        Assert.notEmpty(list, "当前服务没有可用的前端资源");
        StringBuffer res = new StringBuffer();
        // 下载权限表达式
        String tpl = " \n \n  // %s\n static %s  =  '%s'  ";
        res.append("export class Acl{ \n \n ");
        list.forEach(item -> {
            String expr = item.getExpr();
            String exprCode = StrUtil.toSymbolCase(expr, ':').replace(":", "_").toUpperCase();
            String format = String.format(tpl, item.getTitle(), exprCode, expr);
            res.append(format);
        });
        res.append(" \n \n  } ");
        return res.toString();
    }

    /**
     * 获取数据字典下拉选项
     *
     * @param showExpr 是否包含权限表达式
     * @return {@code List<SelectOption>}
     */
    public List<SelectOption> getOptions(boolean showExpr) {
        final String tpl = "[%s]";
        QSystemApi query = new QSystemApi();
        return query.select(QSystemApi.Alias.title, QSystemApi.Alias.token, QSystemApi.Alias.expr)
                .order().sort.asc()
                .findStream()
                .map(item -> {
                    //存在权限表达式且需要显示表达式的时候加上表达式
                    boolean condition = StrUtil.isNotBlank(item.getExpr()) && showExpr;
                    String title = condition ? item.getTitle().concat(String.format(tpl, item.getExpr()))
                            : item.getTitle();
                    return new SelectOption().setValue(item.getToken()).setLabel(title);
                })
                .collect(Collectors.toList());
    }
}
